#include "packet.h"

/* CONSTRUCTORS */
Packet::Packet() {
	set_type(UNDEFINED);
	set_ttl(0);
    set_src_ip(0);
    set_src_port(0);
    set_dest_ip(0);
    set_dest_port(0);
    set_seq(0);
    set_nodes("0");
}

Packet::Packet( char           type,
				unsigned long  ttl,
				unsigned long  src_ip,
				unsigned short src_port,
				unsigned long  dest_ip,
				unsigned short dest_port,
				unsigned long  seq,
				string         nodes ) {
    set_type(type);
    set_ttl(ttl);
    set_src_ip(src_ip);
    set_src_port(src_port);
    set_dest_ip(dest_ip);
    set_dest_port(dest_port);
    set_seq(seq);
    set_nodes(nodes);
    
    //print_packet();
}

/* GETTERS */

char Packet::get_type() {
    return this->type;    
}

unsigned long Packet::get_ttl() {
	return this->ttl;
}

unsigned long Packet::get_src_ip() {
	return this->src_ip;
}

unsigned short Packet::get_src_port() {
	return this->src_port;
}

unsigned long Packet::get_dest_ip() {
	return this->dest_ip;
}

unsigned short Packet::get_dest_port() {
	return this->dest_port;
}

unsigned long Packet::get_seq() {
	return this->seq;
}

string Packet::get_nodes() {
	return this->nodes;
}

/* SETTERS */

void Packet::set_type(char type) {
    if (type != LINK_REQUEST && type != LINK_RESPONSE && type != TRACE &&
    	type != LINK_UPDATE && type != UNDEFINED) {
        cout << "Invalid packet type: " << type << endl;
        exit(EXIT_FAILURE);
    }
    this->type = type;
}

void Packet::set_ttl(unsigned long ttl) {
	this->ttl = ttl;
}

void Packet::set_src_ip(unsigned long src_ip) {
	this->src_ip = src_ip;
}

void Packet::set_src_port(unsigned short src_port) {
	this->src_port = src_port;
}

void Packet::set_dest_ip(unsigned long dest_ip) {
	this->dest_ip = dest_ip;
}

void Packet::set_dest_port(unsigned short dest_port) {
	this->dest_port = dest_port;
}

void Packet::set_seq(unsigned long seq) {
	this->seq = seq;
}

void Packet::set_nodes(string nodes) {
	this->nodes = nodes;
}

/* PRIVATE METHODS */

string Packet::convert_short(unsigned short num) {
	string s = "";
	s += (char) (num >> 8) & 0xFF;
    s += (char) (num >> 0) & 0xFF;
    return s;
}

string Packet::convert_long(unsigned long num) {   
    string s = "";
    s += (char) (num >> 56) & 0xFF;
    s += (char) (num >> 48) & 0xFF;
    s += (char) (num >> 40) & 0xFF;
    s += (char) (num >> 32) & 0xFF;
    s += (char) (num >> 24) & 0xFF;
    s += (char) (num >> 16) & 0xFF;
    s += (char) (num >>  8) & 0xFF;
    s += (char) (num >>  0) & 0xFF;
    return s;
}

/* PUBLIC METHODS */
Packet* Packet::receive_packet(int sockfd, struct sockaddr_in *sockaddr, 
	int msg_size, bool isBlocking) {

	if (!isBlocking) {
		int flags = fcntl(sockfd, F_GETFL);
		flags |= O_NONBLOCK;
		fcntl(sockfd, F_SETFL, flags);
	}
    socklen_t slen = sizeof(struct sockaddr_in);
    char msg[msg_size];
    
    int size = recvfrom(sockfd, msg, msg_size, 0, (struct sockaddr*) sockaddr, &slen);
    if (size == -1) {
		if (!isBlocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
			return NULL;
		}
        perror("Receive error");
    }
    /*else {
        cout << "Received " << size << " bytes" << endl;
    }*/
    
    string s = "";
    for (int i = 0; i < size; i++) {
        //printf("%x ", msg[i]);
        s += msg[i];
    }
    //cout << endl;
    
    return reconstruct_packet(s);
}

void Packet::send_packet(int sockfd, struct sockaddr_in sockaddr) {
    // Put packet into a single string    
    string packet = "";
    packet += this->type;
    packet += convert_long(htonl(this->ttl));
	packet += convert_long(htonl(this->src_ip));
	packet += convert_short(htons(this->src_port));	
	packet += convert_long(htonl(this->dest_ip));
	packet += convert_short(htons(this->dest_port));
	packet += convert_long(htonl(this->seq));
	packet += this->nodes;
    
    int size = sendto(sockfd, packet.c_str(), packet.length(), 0, 
		(struct sockaddr *)&sockaddr, sizeof(sockaddr));
    
    /*if (size == -1) {
        perror("sendto failed");
    }*/
    
    //print_packet();
}

void Packet::print_packet() {
	cout << "Type: "      << this->type      << endl;
	cout << "TTL: "       << this->ttl       << endl;
	cout << "Src IP: "    << this->src_ip    << endl;
	cout << "Src Port: "  << this->src_port  << endl;
	cout << "Dest IP: "   << this->dest_ip   << endl;
	cout << "Dest Port: " << this->dest_port << endl;
	cout << "Seq: "       << this->seq       << endl;
	cout << "Nodes: "     << this->nodes     << endl;
}

Packet* Packet::reconstruct_packet(string packet) {        
	char type = packet[0];
	
	unsigned long ttl = 0;
	for (int i = 0; i < 8; i++) {
		ttl += packet[i+1];
		if (i != 7) ttl = ttl << 8;
	}
	
	ttl = ntohl(ttl);
	
	unsigned long src_ip = 0;
	for (int i = 0; i < 8; i++) {
		src_ip += packet[i+9];
		if (i != 7) src_ip = src_ip << 8;
	}

	src_ip = ntohl(src_ip);

	unsigned short src_port = 0;
	for (int i = 0; i < 2; i++) {
		src_port += packet[i+17];
		if (i != 1) src_port = src_port << 8;
	}

	src_port = ntohs(src_port);

	unsigned long dest_ip = 0;
	for (int i = 0; i < 8; i++) {
		dest_ip += packet[i+19];
		if (i != 7) dest_ip = dest_ip << 8;
	}

	dest_ip = ntohl(dest_ip);

	unsigned short dest_port = 0;
	for (int i = 0; i < 2; i++) {
		dest_port += packet[i+27];
		if (i != 1) dest_port = dest_port << 8;
	}

	dest_port = ntohs(dest_port);
	
	unsigned long seq = 0;
	for (int i = 0; i < 8; i++) {
		seq += packet[i+29];
		if (i != 7) seq = seq << 8;
	}

	seq = ntohl(seq);
	
	string nodes = packet.substr(37);
    
    //cout << "Reconstructed ";
    return new Packet(type, ttl, src_ip, src_port, dest_ip, dest_port, seq, 
    	nodes);
}
